//
// Created by andrew on 2024/9/22.
//
#include <string>
#include <vector>
#include <unordered_set>

#include <iostream>
#include <gtest/gtest.h>


using namespace std;


/*

复杂度分析
时间复杂度：O(r⋅n)，其中 r=numRows，n 为字符串 s 的长度。时间主要消耗在矩阵的创建和遍历上，矩阵的行数为 r，列数可以视为 O(n)。
空间复杂度：O(r⋅n)。矩阵需要 O(r⋅n) 的空间。
 * */

// 将一列，和斜的地方作为一个整体
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.length(), r = numRows;
        if (r == 1 || r >= n) {
            return s;
        }
        int t = r * 2 - 2;
        int c = (n + t - 1) / t * (r - 1);
        vector<string> mat(r, string(c, 0));

        for (int i = 0, x = 0, y = 0; i < n; ++i) {
            mat[x][y] = s[i];

            // 判断时，对应的字符已经进入到矩阵中，这个时候需要直接进行转向
            if (i % t < r - 1) {
                ++x; // 向下移动
            } else {
                --x;
                ++y; // 向右上移动
            }
        }
        string ans;
        for (auto &row : mat) {
            for (char ch : row) {
                if (ch) {
                    ans += ch;
                }
            }
        }
        return ans;
    }
};

/*
方法二：压缩矩阵空间
方法一中的矩阵有大量的空间没有被使用，能否优化呢？
注意到每次往矩阵的某一行添加字符时，都会添加到该行上一个字符的右侧，且最后组成答案时只会用到每行的非空字符。因此我们可以将矩阵的每行初始化为一个空列表，每次向某一行添加字符时，添加到该行的列表末尾即可。
 * */

class Solution1 {
public:
    string convert(string s, int numRows) {
        int n = s.length(), r = numRows;
        if (r == 1 || r >= n) {
            return s;
        }
        vector<string> mat(r);
        for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
            mat[x] += s[i];
            i % t < r - 1 ? ++x : --x;
        }
        string ans;
        for (auto &row : mat) {
            ans += row;
        }
        return ans;
    }
};


class Solution3 {
public:
    string convert(string s, int numRows) {
        int n = s.length(), r = numRows;

        if (r == 1 || r >= n) {
            return s;
        }

        vector<string> mat(r);
        for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) {
            mat[x] += s[i];
            i % t < r - 1 ? ++x : --x;
        }

        string ans;

        for (auto& row : mat) {
            ans += row;
        }

        return ans;
    }
};



/*
 方法三：直接构造
我们来研究方法一中矩阵的每个非空字符会对应到 s 的哪个下标（记作 idx），从而直接构造出答案。

由于 Z 字形变换的周期为 t=2r−2，因此对于矩阵第一行的非空字符，其对应的 idx 均为 t 的倍数，即 idx≡0(modt)；同理，对于矩阵最后一行的非空字符，应满足 idx≡r−1(modt)。

对于矩阵的其余行（行号设为 i），每个周期内有两个字符，第一个字符满足 idx≡i(modt)，第二个字符满足 idx≡t−i(modt)。

 * */

class Solution2 {
public:
    string convert(string s, int numRows) {
        int n = s.length(), r = numRows;
        if (r == 1 || r >= n) {
            return s;
        }
        string ans;
        int t = r * 2 - 2;
        for (int i = 0; i < r; ++i) { // 枚举矩阵的行
            for (int j = 0; j + i < n; j += t) { // 枚举每个周期的起始下标
                ans += s[j + i]; // 当前周期的第一个字符
                if (0 < i && i < r - 1 && j + t - i < n) {
                    ans += s[j + t - i]; // 当前周期的第二个字符
                }
            }
        }
        return ans;
    }
};


TEST(leetcode6, leetcode) {


    Solution solution;
    EXPECT_STREQ("a" , solution.convert("PAYPALISHIRING", 3).c_str());


}
